;(function(){
'use strict';
var ID=0;
var DOM = typeof(window)!='undefined';
var HEX = '0123456789abcdef'
	, N = [
		0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
		0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
		0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
		0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
		0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
		0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
		0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
		0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391,
		//MD5:A,B,C,D
		0x67452301,0xefcdab89,0x98badcfe,0x10325476,
		//SHA1:A,B,C,D,E
		0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0
	]
	, MD5 = {
		F: [7,12,17,22],
		G: [5, 9,14,20],
		H: [4,11,16,23],
		I: [6,10,15,21],
		f: function(A,x,s,t){return CMN((A[1]&A[2])|((~A[1])&A[3]),A[0],A[1],x,s,t);},
		g: function(A,x,s,t){return CMN((A[1]&A[3])|(A[2]&(~A[3])),A[0],A[1],x,s,t);},
		h: function(A,x,s,t){return CMN(A[1]^A[2]^A[3],A[0],A[1],x,s,t);},
		i: function(A,x,s,t){return CMN(A[2]^(A[1]|(~A[3])),A[0],A[1],x,s,t);},
		x: function(o,O){
			O[0]=o[0]=ADD(o[0],O[0]);
			O[1]=o[1]=ADD(o[1],O[1]);
			O[2]=o[2]=ADD(o[2],O[2]);
			O[3]=o[3]=ADD(o[3],O[3]);
		},
		$: function(A,M,f,p,x,s,m,n){
			var i=0,j=f;
			for(;i<16;i++){
				A[0]=M(A,x[n+j],s[i%4],N[i+m]);
				A.unshift(A.pop());
				j=(j+p)%16;
			}
			i=j=null;
		}
	}
	;

function isFunction(v){ return typeof(v) == 'function'; }
function getId(){return ++ID;}
var encrypt = { getId: getId };


/* MD5 */
function STR2ARR(s){
	var a=[],i=0,l=s.length;
	for(;i<l;i++)a.push(s.charCodeAt(i));
	i=l=null;
	return a;
}
function ADD(x,y){
	var l=(x&0xFFFF)+(y&0xFFFF),m=(x>>16)+(y>>16)+(l>>16),r=(m<<16)|(l&0xFFFF);
	l=m=null;
	return r;
}
function ROL(num,cnt){return (num<<cnt)|(num>>>(32-cnt));}
function CMN(q,a,b,x,s,t){return ADD(ROL(ADD(ADD(a, q), ADD(x, t)), s),b);}
function CORE_MD5(x,o){
	var i=0,o=o||N.slice(64,68),O=o.slice(0);
	for(;i<x.length;i+=16){
 		MD5.$(o,MD5.f,0,1,x,MD5.F,0,i);
 		MD5.$(o,MD5.g,1,5,x,MD5.G,16,i);
 		MD5.$(o,MD5.h,5,3,x,MD5.H,32,i);
 		MD5.$(o,MD5.i,0,7,x,MD5.I,48,i);
 		MD5.x(o, O);
	}
	O=i=x=null;
	return o;
}
function BINL2HEX(bin){
	var str='',i=0;
	for(;i<bin.length*4;i++)
		str+=HEX.charAt((bin[i>>2]>>((i%4)*8+4))&15)
			+HEX.charAt((bin[i>>2]>>((i%4)*8))&15)
			;
	return str;
}
function ARR2BINL(a){
	var r=[],i=0,l=a.length*8;
	for(;i<l;i+=8)r[i>>5]|=(a[i>>3]&255)<<(i%32);
	i=l=null;
	return r;
}
function ARR2BINL_FIN(binl,l,L){
	l*=8;
	L*=8;
	binl[l>>5]|=0x80<<((L)%32);
	binl[(((l+64)>>>9)<<4)+14]=L;
}
encrypt.MD5=function(s){
	if(typeof(ArrayBuffer)=='function' && s instanceof ArrayBuffer) s = new Uint8Array(s);
	else if(!(s instanceof Array)) s = STR2ARR(s + '');
	var r = ARR2BINL(s);
	ARR2BINL_FIN(r, s.length, s.length);
	r = CORE_MD5(r);
	s = null;
	return r;
};
encrypt.md5=function(s){
	s = this.MD5(s);
	s = BINL2HEX(s);
	return s;
};

/* SHA1 */
var SHA1 = {
	w: function(x,w,o,i){
		for (var j=0;j<80;j++) {
			if(j<16)w[j]=x[i+j];
			else w[j]=ROL(w[j-3]^w[j-8]^w[j-14]^w[j-16],1);
			var t=ADD(ADD(ROL(o[0],5),SHA1_FT(j,o[1],o[2],o[3])),ADD(ADD(o[4],w[j]),SHA1_KT(j)));
			o[4]=o[3];
			o[3]=o[2];
			o[2]=ROL(o[1],30);
			o[1]=o[0];
			o[0]=t;
		}
	},
	x: function(o,O){
		O[0]=o[0]=ADD(o[0],O[0]);
		O[1]=o[1]=ADD(o[1],O[1]);
		O[2]=o[2]=ADD(o[2],O[2]);
		O[3]=o[3]=ADD(o[3],O[3]);
		O[4]=o[4]=ADD(o[4],O[4]);
	}
};
function BINB2HEX(bin) {
	var str='',i=0;
	for(;i<bin.length*4;i++) {
		str+=HEX.charAt((bin[i>>2]>>((3-i%4)*8+4))&15)
			+HEX.charAt((bin[i>>2]>>((3-i%4)*8))&15);
	}
	return str;
}
function CORE_SHA1(x,o) {
	var w=Array(80),o=o||N.slice(68,73),O=o.slice(0),i=0;
	for (;i<x.length;i+=16) {
		SHA1.w(x,w,o,i);
		SHA1.x(o,O);
	}
	return o;
}
function ARR2BINB(a) {
	var r=[],i=0,l=a.length*8;
	for(;i<l;i+=8)r[i>>5]|=(a[i>>3]&255)<<(24-i%32);
	return r;
}
function ARR2BINB_FIN(binb, l, L){
	l*=8;
	L*=8;
	binb[l>>5]|=0x80<<(24-L%32);
	binb[((l+64>>9)<<4)+15]=L;
}
function SHA1_FT(t,b,c,d) {
	if(t<20)return(b&c)|((~b)&d);
	if(t<40)return b^c^d;
	if(t<60)return(b&c)|(b&d)|(c&d);
	return b^c^d;
}
function SHA1_KT(t){return (t<20)?0x5A827999:(t<40)?0x6ED9EBA1:(t<60)?0x8F1BBCDC:0xCA62C1D6;}
encrypt.SHA1 = function(s){
	if(typeof(ArrayBuffer) == 'function' && s instanceof ArrayBuffer) s = new Uint8Array(s);
	else if(!(s instanceof Array)) s = STR2ARR(s + '');
	var r = ARR2BINB(s);
	ARR2BINB_FIN(r, s.length, s.length);
	r = CORE_SHA1(r);
	s = null;
	return r;
};
encrypt.sha1 = function(s){
	s=this.SHA1(s);
	s=BINB2HEX(s);
	return s;
};

function HashHelper(length, core, bin, fin, hex){
	var R = {
		fin: function(l){
			fin(this._bin, l, length);
		},
		hash: function(a){
			this._bin = bin(a);
		},
		cal: function(){
			this._result = core(this._bin, this._result);
			delete this._bin;
		},
		hex: function(){
			return hex(this._result);
		},
		dispose: function(){
			for(var p in this){
				delete this[p];
			}
		}
	};
	return R;
};
HashHelper.SHA1 = function(len){
	var hasher = HashHelper(len, CORE_SHA1, ARR2BINB, ARR2BINB_FIN, BINB2HEX);
	hasher.type = 'sha1';
	return hasher;
};
HashHelper.MD5 = function(len){
	var hasher = HashHelper(len, CORE_MD5, ARR2BINL, ARR2BINL_FIN, BINL2HEX);
	hasher.type = 'md5';
	return hasher;
};
encrypt.createHelper = function(type, len){
	type = (type || '').toUpperCase();
	if(type in HashHelper && isFunction(HashHelper[type])){
		return HashHelper[type](len);
	}
	throw 'The ' + type + ' hash is not supported'
};


// encrypt.createReader = function(blob){
// 	var reader = new BlobReader(blob);
// 	return reader;
// };

if(DOM){
	window.encrypt = encrypt;
}

})();